import hashlib
import os
from shutil import move as move2
from shutil import copyfile
from .common_func.file_open import FileOpen
from .common_func.print_logger import PrintLogger


class UFile:
    __logger = PrintLogger

    @classmethod
    def _reg_logger(cls, logger):
        """
        <func desc>注册日志句柄</func desc>
        <func usage>注册日志句柄</func usage>
        :param logger: <param desc>日志句柄</param desc>
        :return:
        """
        cls.__logger = logger

    @classmethod
    def _check_small_file(cls, file_path):
        """
        通过for循环以字节流读取文件并且通过update方法添加到md5对象中进行校验
        :param file_path: 文件路径
        :return:
        """
        cls.__logger.info(f"开始校验文件:{file_path}")
        _obj = hashlib.md5()
        with open(file_path, "rb") as f:
            data = f.read()
            _obj.update(data)
        md5_res = _obj.hexdigest()
        cls.__logger.info(f"文件:{file_path} MD5校验结果为:{md5_res}")
        return md5_res

    @classmethod
    def _check_big_file(cls, file_path, check_freq: int = 20, once_bytes=100):
        """
        该方法用于大文件校验
        通过seek方法移动文件指针
        保证对文件每5%的地方进行一次读取,并校验其完整性！
        :tips 如果文件过小,可能会出现校验出错的文件,所以该方法一般用于大型文件校验
        :param file_path 文件路径
        :param check_freq 校验次数默认20,填写几次则会分个区间去读取并校验
        :check_once_bytes 单次校验字节,默认100个字节
        :return:
        """
        # 获取文件的字节数(方法1)
        file_bytes = os.path.getsize(file_path)
        enc_obj = hashlib.md5()
        with open(file_path, 'rb') as f:
            check_bytes = file_bytes // check_freq  # 每次移动的文件字节数
            for i in range(check_freq):
                f.seek(check_bytes * i, 0)
                enc_obj.update(f.read(once_bytes))
        md5_res = enc_obj.hexdigest()
        cls.__logger.info(f"文件:{file_path} MD5校验结果为:{md5_res}")
        return md5_res

    @classmethod
    def get_file_md5(cls, file_path):
        """
        获取文件MD5
        """
        file_bytes = os.path.getsize(file_path)
        if file_bytes >= 1024 * 1024:   # 1M
            return cls._check_big_file(file_path)
        else:
            return cls._check_small_file(file_path)

    @classmethod
    def get_size(cls, path):
        """
        获取文件夹大小，单位是比特
        :param path:
        :return:
        """
        if os.path.isfile(path):
            return os.path.getsize(path)

        _size = 0
        for file_name in os.listdir(path):
            _path = os.path.join(path, file_name)
            if os.path.isfile(_path):
                _size += os.path.getsize(_path)
            elif os.path.isdir(_path):
                _size += cls.get_size(_path)
        return _size

    @classmethod
    def create_random_file(cls, file_path: str = "", size_unit: str = "K", file_size: int = 0):
        """
        按照指定大小创建随机内容的文件,成功返回1,失败返回0
        
        :param file_path:
        :param size_unit:
        :param file_size:
        :return:
        """
        if os.name == "nt":
            raise ValueError("System type NT is not support")

        size_unit = size_unit.upper()
        if size_unit not in ["B", "K", "M"]:
            raise SyntaxError("文件大小的单位只支持输入B/K/M")
        if size_unit == "B":
            size_unit = ""
        if not file_path:
            raise SyntaxError("未给出文件路径")
        command = f"dd if=/dev/zero of={file_path} bs=1{size_unit} count={file_size}"
        last_result = os.system(command)
        if last_result:
            cls.__logger.error(f"{file_path}创建失败")
        else:
            cls.__logger.info(f"OK！， {file_path}创建成功")

        return last_result

    @classmethod
    def move_file_or_dir(cls, origin_path: str = "", dst_dir: str = ""):
        """
        移动文件或文件夹,成功返回1,失败返回0

        :param origin_path:
        :param dst_dir:
        :return:
        """
        if not os.path.exists(origin_path):
            raise SyntaxError(f"移动文件(夹)时给出的源路径不存在, 源路径：{origin_path}")
        if not os.path.exists(dst_dir):
            raise SyntaxError(f"移动文件(夹)时给出的目标文件夹不存在，目标文件夹：{dst_dir}")

        base_name = os.path.basename(origin_path)
        dst_path = os.path.join(dst_dir, base_name)
        last_result = 0
        try:
            move2(origin_path, dst_dir)
            if os.path.exists(dst_path):
                last_result = 1
                cls.__logger.info("OK！，移动文件(夹)创建成功")
        except Exception as e:
            cls.__logger.error(f"移动文件(夹)时出现错误，错误信息：{e}, error line:{e.__traceback__.tb_lineno}")

        return last_result

    @classmethod
    def copy_file(cls, src_file: str = "", dst_file: str = "", force=True):
        """
        移动文件或文件夹,成功返回1,失败返回0

        :param src_file:
        :param dst_file:
        :param force 强制覆盖标志
        :return:
        """
        if not os.path.exists(src_file):
            raise SyntaxError(f"源文件不存在, 源路径：{src_file}")
        if os.path.exists(dst_file) and force:
            os.remove(dst_file)
        copyfile(src_file, dst_file)

    @classmethod
    def file_encrypt(cls, file_path):
        with FileOpen(file_path) as f:
            data = f.read()
        with FileOpen(file_path, "wb", encrypt_mode="aes") as f:
            f.write(data)

    @classmethod
    def file_decrypt(cls, file_path):
        with FileOpen(file_path) as f:
            data = f.read()
        with open(file_path, "wb") as f:
            f.write(data)
